﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BackupAssurance.Library.MEF;
using System.ComponentModel.Composition;
using BackupAssurance.Library.ModelsDB;
using BackupAssurance.Library.Interfaces;
using BackupAssurance.Library.XML;
using BackupAssurance.Library.Constants;
using System.IO;
using BackupAssurance.Library.Enums;

namespace BackupAssurance.impFileDelete
{

    [Export(typeof(IExecute)), ExportMetadata("PlugInType", "DeleteOldFiles"), ExportMetadata("Version", 1)]
    public class piFileDelete : IExecute
    {

        [Import]
        public IfLogger objLog { get; set; }

        string _name = null;
        //ID	Name	Description	Folder	FilePattern	NumberDaysOld	FileExtension
        //1	DeleteLocal	deletes old spreadsheets	C:\junk	inventory	15	xlsx

        //private string _PlugInType = "FileDelete";
        //private int _Version = 1;

        string IncludeFilenames = "";
        string IncludeFolders = "";
        string IncludeExtensions = "";
        string ExcludeFilenames = "";
        string ExcludeFolders = "";
        string ExcludeExtensions = "";

        string[] aIncludeFilenames;
        string[] aIncludeFolders;
        string[] aIncludeExtensions;
        string[] aExcludeFilenames;
        string[] aExcludeFolders;
        string[] aExcludeExtensions;

        Boolean DeleteEmptyFolders;
        string ProcessFolder;
        int NumDaysDeleteBack;
        Boolean RecurseFolder;
        int NumFilesDeleted;
        int NumFoldersdeleted;
        StringBuilder objstring;
        List<String> objFoldersToDelete = null;
        int _DemoMode = 0;
        //<resource key="DeleteEmptyFolders">True</resource>
        //<resource key="ProcessFolder">C:\Temp</resource>
        //<resource key="NumDaysDeleteBack">6</resource>
        //<resource key="RecurseFolder">True</resource>
        //<resource key="IncludeFilenames">temp*;tmp*</resource>
        //<resource key="IncludeFolders">temp*;tmp*</resource>
        //<resource key="IncludeExtensions">bak*;tmp*</resource>
        //<resource key="ExcludeFilenames">temp*;tmp*</resource>
        //<resource key="ExcludeFolders">temp*;tmp*</resource>
        //<resource key="ExcludeExtensions">bak*;tmp*</resource> 

        public string Execute(pcJobAction jobaction, pcPlugInValue plugininfo)
        {
            string strResult = "";
            DeleteEmptyFolders = false;
            ProcessFolder = "";
            NumDaysDeleteBack = 0;
            RecurseFolder = false;
            NumFilesDeleted = 0;
            NumFoldersdeleted = 0;
            RecurseFolder = false;
            objFoldersToDelete = new List<String>();
            int _DemoMode = 0;
            objstring = new StringBuilder();
            string output;

            _DemoMode = plugininfo.DemoMode;

            _name = " START, Computer:" + System.Environment.MachineName + " DeleteOldFiles v1,  Job ID:" + jobaction.Id.ToString() + " Plugin ID:" + plugininfo.Id.ToString() + Environment.NewLine + "       [" + plugininfo.PlugInText + "]";

            objstring.AppendLine("");
            objstring.AppendLine("");
            objstring.AppendLine("Files to Delete today [" + DateTime.Now + "]");
            objstring.AppendLine("--------------------------------------------------");
            try
            {
                //grab the fields from the XML or whatever.
                Dictionary<string, string> objDic;
                objLog.WriteLine("Plugin Start: " + _name);
                //objLog.WriteLine(plugininfo.PlugInText);
                objDic = fXMLParser.ConvertXMLtoDictionary(plugininfo.PlugInText);

                DeleteEmptyFolders = Boolean.Parse(objDic[Constants.PLG_DELETEEMPTYFOLDERS]);
                ProcessFolder = objDic[Constants.PLG_PROCESSFOLDER];
                NumDaysDeleteBack = Int32.Parse(objDic[Constants.PLG_NUMDAYSDELETEBACK]);
                RecurseFolder = Boolean.Parse(objDic[Constants.PLG_RECURSEFOLDER]);

                if (ProcessFolder.Substring(ProcessFolder.Length - 1, 1) != "\\")
                {
                    ProcessFolder = ProcessFolder + "\\";
                }

                IncludeFilenames = objDic[Constants.PLG_INCLUDEFILENAMES];
                IncludeFolders = objDic[Constants.PLG_INCLUDEFOLDERS];
                IncludeExtensions = objDic[Constants.PLG_INCLUDEEXTENSIONS];
                ExcludeFilenames = objDic[Constants.PLG_EXCLUDEFILENAMES];
                ExcludeFolders = objDic[Constants.PLG_EXCLUEFOLDERS];
                ExcludeExtensions = objDic[Constants.PLG_EXCLUDEEXTENSIONS];

                IncludeFilenames = IncludeFilenames.Trim().ToUpper();
                IncludeFolders = IncludeFolders.Trim().ToUpper();
                IncludeExtensions = IncludeExtensions.Trim().ToUpper();
                ExcludeFilenames = ExcludeFilenames.Trim().ToUpper();
                ExcludeFolders = ExcludeFolders.Trim().ToUpper();
                ExcludeExtensions = ExcludeExtensions.Trim().ToUpper();

                IncludeExtensions.Replace(".", "");
                ExcludeExtensions.Replace(".", "");

                //put the inclusion and exclusions into arrays
                aIncludeFilenames = IncludeFilenames.Split(';');
                aIncludeFolders = IncludeFolders.Split(';');
                aIncludeExtensions = IncludeExtensions.Split(';');

                aExcludeFilenames = ExcludeFilenames.Split(';');
                aExcludeFolders = ExcludeFolders.Split(';');
                aExcludeExtensions = ExcludeExtensions.Split(';');

                //if (DeleteEmptyFolders)
                //{
                //if we are going to delete empty folders, then initialize the array that holds the list of folders to delete
                objFoldersToDelete = new List<String>();
                //}

                try
                {
                    //check if the folder exists
                    if (!Directory.Exists(ProcessFolder))
                    {
                        output = _name + Environment.NewLine + "Process Folder [" + ProcessFolder + "] does not exist.";
                        objLog.WriteError(0, output);
                        throw new Exception(output);
                    }
                }
                catch (Exception)
                {
                    output = _name + Environment.NewLine + "Process Folder [" + ProcessFolder + "] does not exist.";
                    objLog.WriteError(0, output);
                    throw new Exception(output);
                }


                try
                {
                    //ok, start to look for files/folders to delete
                    objLog.WriteLine("Looking for files to delete...");
                    DirectoryInfo di = new DirectoryInfo(ProcessFolder);
                    WalkDirectoryTree(di);
                }
                catch (Exception e)
                {
                    output = _name + Environment.NewLine + " Unhandled Error trying to determine which files to delete." + Environment.NewLine + e.Message + Environment.NewLine + e.StackTrace;
                    objLog.WriteError(0, output);
                    throw new Exception(output);
                }

                if (DeleteEmptyFolders)
                {
                    objstring.AppendLine("");
                    objstring.AppendLine("");
                    objstring.AppendLine("Empty folders to Delete");
                    objstring.AppendLine("--------------------------------------------------");
                    objLog.WriteLine("Identifying empty folders that can be deleted.");
                    if (objFoldersToDelete.Count >= 0)
                    {
                        //remove duplicates, shrink the list
                        List<string> objFolders = removeDuplicates(objFoldersToDelete);

                        //ensure that childre folders are deleted before parent folders
                        objFolders.Reverse();

                        //delete the folders
                        foreach (string strfolder in objFolders)
                        {
                            if (IsDirectoryEmpty(strfolder))
                            {
                                NumFoldersdeleted = NumFoldersdeleted + 1;
                                objstring.AppendLine("Deleting Empty Folder [" + strfolder + "]");
                                if (_DemoMode == 0)
                                {
                                    Directory.Delete(strfolder);
                                }
                            }
                        }
                    }
                }
                objstring.AppendLine("");
                objstring.AppendLine("");
                objstring.AppendLine("Totals");
                objstring.AppendLine("--------------------------------------------------");
                objstring.AppendLine("Files Deleted:" + NumFilesDeleted.ToString());
                objstring.AppendLine("Folders Deleted:" + NumFoldersdeleted.ToString());
                objstring.AppendLine("");
                objstring.AppendLine("");
                objstring.AppendLine("--------------------------------------------------");
                objstring.AppendLine(_name);
                strResult = objstring.ToString();
            }
            catch (Exception ex)
            {
                output = _name + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace;
                objLog.WriteError(0, output);
                throw new Exception(output);

            }

            return strResult;

        }

        void WalkDirectoryTree(System.IO.DirectoryInfo root)
        {
            string output;
            System.IO.FileInfo[] files = null;
            System.IO.DirectoryInfo[] subDirs = null;

            // First, process all the files directly under this folder
            try
            {
                files = root.GetFiles("*.*");
            }
            // This is thrown if even one of the files requires permissions greater
            // than the application provides.
            catch (UnauthorizedAccessException e)
            {
                // This code just writes out the message and continues to recurse.
                // You may decide to do something different here. For example, you
                // can try to elevate your privileges and access the file again.
                output = _name + Environment.NewLine + " Error:Unauthorized Access.  " + root.ToString() + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                objLog.WriteError(0, output);
            }

            catch (System.IO.DirectoryNotFoundException e)
            {
                output = _name + Environment.NewLine + " Error:Directory Not Found.  " + root.ToString() + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                objLog.WriteError(0, output);
            }

            if (files != null)
            {
                foreach (System.IO.FileInfo fi in files)
                {
                    // In this example, we only access the existing FileInfo object. If we
                    // want to open, delete or modify the file, then
                    // a try-catch block is required here to handle the case
                    // where the file has been deleted since the call to TraverseTree().

                    try
                    {
                        ProcessFile(fi);
                    }
                    catch (Exception e)
                    {
                        output = _name + Environment.NewLine + " Unhandled Error Professing File  [" + fi.FullName + "] " + Environment.NewLine + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                        objLog.WriteError(0, output);
                        //throw new Exception(output);
                    }
                }

                // Now find all the subdirectories under this directory.
                subDirs = root.GetDirectories();
                if (RecurseFolder)
                {
                    foreach (System.IO.DirectoryInfo dirInfo in subDirs)
                    {
                        // Resursive call for each subdirectory.
                        WalkDirectoryTree(dirInfo);
                    }
                }

            }
        }

        public bool IsDirectoryEmpty(string path)
        {
            return !Directory.EnumerateFileSystemEntries(path).Any();
        }

        private void ProcessFile(FileInfo file)
        {

            //            string IncludeFilenames = "";
            //            string IncludeFolders = "";
            //            string IncludeExtensions = "";
            //            string ExcludeFilenames = "";
            //            string ExcludeFolders = "";
            //            string ExcludeExtensions = "";

            //Console.WriteLine(file.Extension);
            //Console.WriteLine(file.DirectoryName);
            //Console.WriteLine(file.Name);
            //Console.WriteLine(file.FullName);

            if (FileIsIncluded(file) && FileIsNotExcluded(file))
            {
                //now check how old the file is:
                TimeSpan span = DateTime.Now - file.LastWriteTime;// .LastAccessTime;
                double DaysOld = span.TotalDays;
                if (DaysOld >= NumDaysDeleteBack)
                {
                    //test the folder name
                    if (DeleteEmptyFolders)
                    {
                        objFoldersToDelete.Add(file.DirectoryName);
                    }
                    NumFilesDeleted = NumFilesDeleted + 1;
                    string output = String.Format("Deleting File [{0}], [{1:0.00}] days old and last updated [{2}]", file.FullName, DaysOld, file.LastWriteTime.ToString());
                    objstring.AppendLine(output);

                    if (_DemoMode == 0)
                    {
                        File.Delete(file.FullName);
                    }
                }
            }
        }

        List<string> removeDuplicates(List<string> inputList)
        {
            Dictionary<string, int> uniqueStore = new Dictionary<string, int>();
            List<string> finalList = new List<string>();

            foreach (string currValue in inputList)
            {
                if (!uniqueStore.ContainsKey(currValue))
                {
                    uniqueStore.Add(currValue, 0);
                    finalList.Add(currValue);
                }
            }
            return finalList;
        }


        private Boolean FileIsIncluded(FileInfo file)
        {
            Boolean bIncludeFilenames = false;
            Boolean bIncludeFolders = false;
            Boolean bIncludeExtensions = false;


            try
            {
                if (IncludeFilenames == "" || IncludeFilenames == "*")
                {
                    bIncludeFilenames = true;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bIncludeFilenames = false;
                    }
                    else
                    {
                        string FileName = file.Name.Replace(file.Extension, "");
                        FileName = FileName.ToUpper();
                        bIncludeFilenames = ArrayCompare(FileName, aIncludeFilenames);
                    }
                }

                if (IncludeFolders == "" || IncludeFolders == "*")
                {
                    bIncludeFolders = true;
                }
                else
                {
                    bIncludeFolders = ArrayCompare(file.DirectoryName.ToUpper(), aIncludeFolders);
                }

                if (IncludeExtensions == "" || IncludeExtensions == "*")
                {
                    bIncludeExtensions = true;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bIncludeExtensions = false;
                    }
                    else
                    {
                        string sExtension = file.Extension.ToUpper();

                        if ((sExtension != "") && sExtension.Substring(0, 1) == ".")
                        {
                            sExtension = sExtension.Substring(1);
                        }

                        bIncludeExtensions = ArrayCompare(sExtension, aIncludeExtensions);
                    }
                }
                //Console.WriteLine("{0}, fld:{1}, fle:{2}, ext:{3}", file.Name, bIncludeFolders, bIncludeFilenames, bIncludeExtensions);
            }
            catch (Exception ex)
            {
                string output = _name + Environment.NewLine + " piFileDelete.FileIsIncluded " + file.FullName + " " + ex.Message + Environment.NewLine + ex.StackTrace;
                objLog.WriteError(0, output);
            }


            //aggregate check
            if ((bIncludeFilenames) && (bIncludeFolders) && (bIncludeExtensions))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private Boolean FileIsNotExcluded(FileInfo file)
        {
            Boolean bExcludeFilenames = false;
            Boolean bExcludeFolders = false;
            Boolean bExcludeExtensions = false;

            try
            {
                //check the filename
                if (ExcludeFilenames == "" || ExcludeFilenames == "*")
                {
                    bExcludeFilenames = false;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bExcludeFilenames = false;
                    }
                    else
                    {
                        string FileName = file.Name.Replace(file.Extension, "");
                        FileName = FileName.ToUpper();
                        bExcludeFilenames = ArrayCompare(FileName, aExcludeFilenames);
                    }
                }

                //check the folder
                if (ExcludeFolders == "" || ExcludeFolders == "*")
                {
                    bExcludeFolders = false;
                }
                else
                {
                    bExcludeFolders = ArrayCompare(file.DirectoryName.ToUpper(), aExcludeFolders);
                }

                //check the extension
                if (ExcludeExtensions == "" || ExcludeExtensions == "*")
                {
                    bExcludeExtensions = false;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bExcludeExtensions = false;
                    }
                    else
                    {
                        string Extension = file.Extension.Substring(1);
                        Extension = Extension.ToUpper();
                        bExcludeExtensions = ArrayCompare(Extension, aExcludeExtensions);
                    }
                }

            }
            catch (Exception ex)
            {
                string output = _name + Environment.NewLine + " piFileDelete.FileIsNotExcluded " + file.FullName + " " + ex.Message + Environment.NewLine + ex.StackTrace;
                objLog.WriteError(0, output);
            }

            //Console.WriteLine("{0}, fld:{1}, fle:{2}, ext:{3}", file.Name, bExcludeFilenames, bExcludeFolders, bExcludeExtensions);

            //aggregate check
            if ((bExcludeFilenames) || (bExcludeFolders) || (bExcludeExtensions))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private Boolean ArrayCompare(string strInFileInfo, string[] thearray)
        {

            TypeFileMask enumMask;
            String strNewfromXML;
            int ilength = 0;
            int iInputLength = strInFileInfo.Length;
            strInFileInfo = strInFileInfo.ToUpper();
            //Boolean returnvalue = false;

            if (strInFileInfo == "")
            {
                return false;
            }

            foreach (string filterfromxml in thearray)
            {
                if (filterfromxml == "")
                {
                    continue;
                }
                enumMask = determinemask(filterfromxml);
                strNewfromXML = filterfromxml.Replace("*", "");
                ilength = strNewfromXML.Length;

                switch (enumMask)
                {
                    case TypeFileMask.none:
                        if (strInFileInfo == strNewfromXML) { return true; }
                        break;
                    case TypeFileMask.left:
                        if (ilength <= iInputLength)
                        {
                            if (strInFileInfo.Substring(iInputLength - ilength, ilength) == strNewfromXML)
                            {
                                return true;
                            }
                        }
                        break;
                    case TypeFileMask.right:
                        if (ilength <= iInputLength)
                        {
                            if (strInFileInfo.Substring(0, ilength) == strNewfromXML)
                            {
                                return true;
                            }
                        }
                        break;
                    case TypeFileMask.both:
                        if (strInFileInfo.IndexOf(strNewfromXML) >= 0)
                        {
                            return true;
                        }
                        break;
                    default:
                        return false;
                }

            }
            return false;
        }

        public void Dispose()
        {

        }


        private TypeFileMask determinemask(string strInput)
        {


            Boolean leftmostison = false;
            Boolean rightmostison = false;
            int lenght = strInput.Length;

            if (strInput.Substring(0, 1) == "*") { leftmostison = true; }
            if (strInput.Substring(lenght - 1, 1) == "*") { rightmostison = true; }

            if ((leftmostison == false) && (rightmostison == false))
            {
                return TypeFileMask.none;
            }
            else if ((leftmostison == true) && (rightmostison == true))
            {
                return TypeFileMask.both;
            }
            else if ((leftmostison == true) && (rightmostison == false))
            {
                return TypeFileMask.left;
            }
            else if ((leftmostison == false) && (rightmostison == true))
            {
                return TypeFileMask.right;
            }
            else
            {
                //should never happen
                return TypeFileMask.none;
            }
        }
    }
}
